#region License

/*
  GShield - Game Shield
  Copyright (C) 2006-2007 WaveTech Digital Technologies, Inc.
                                http://www.wtdt.com

  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/

#endregion License

#region Includes

using System.Configuration;
using System.IO;
using System.Net;
using System.Web;
using WaveTech.GShield.Data;
using WaveTech.GShield.Engine.Tools;
using WaveTech.GShield.Model;
using WaveTech.GShield.Model.Interfaces;

#endregion

namespace WaveTech.GShield.Engine.Managers
{
	public class ESRBManager : IEsrbManager
	{
		private string name;
		private bool TrimNonExactRatingResults;

		#region Constructor

		#endregion Constructor

		#region Is Rating TableRow

		/*
     * NOTE:
     *      As of the current version, this will only
     *      look at the first page of results. It's
     *      my hope that the ESRP will put up a web
     *      service of their own to correct this issue.
     */

		/// <summary>
		/// Determines if the table row is a ESRB Game rating row
		/// </summary>
		/// <param name="inputLine">Input line from a stream</param>
		/// <returns>True if this is a rating row, else false.</returns>
		public bool IsRatingTableRow(string inputLine)
		{
			if ((inputLine.IndexOf("<tr class=\"oddSearchResultRow\">") != -1) ||
			    (inputLine.IndexOf("<tr class=\"evenSearchResultRow\">") != -1))
				return true;
			else
				return false;
		}

		#endregion Is Rating TableRow

		#region Process Rating TableRow

		/// <summary>
		/// Extracts the Game Rating information from the stream.
		/// </summary>
		/// <param name="reader">Stream to Parse</param>
		/// <returns>GameRatingDetail object with rating information</returns>
		public GameRatingDetail ProcessRatingTableRow(StreamReader reader)
		{
			int i = 0;
			GameRatingDetail rating = new GameRatingDetail();

			while (i < 5)
			{
				string line = reader.ReadLine();

				switch (i)
				{
					case 0: // Name of the game
						rating.Name = line.Trim().Remove(0, 4).Remove((line.Trim().Length - 9), 5).Trim();
						break;
					case 1: // Publisher of the game
						rating.Publisher = line.Trim().Remove(0, 4).Remove((line.Trim().Length - 9), 5).Trim();
						break;
					case 2: // The games rating
						rating.Rating = line.Trim().Remove(0, 19).Remove((line.Trim().Length - 24), 5).Trim();
						break;
					case 3: // Content in the game
						rating.Content = line.Trim().Remove(0, 4).Remove((line.Trim().Length - 9), 5).Trim();
						break;
					case 4: // Platforms of the game
						rating.Platforms = line.Trim().Remove(0, 4).Remove((line.Trim().Length - 9), 5).Trim();
						break;
				}

				i++;
			}


			if ((rating.Name != null) && (rating.Publisher != null) &&
			    (rating.Rating != null) && (rating.Content != null) &&
			    (rating.Platforms != null))
			{
				return rating;
			}
			else
				return null;
		}

		#endregion Process Rating TableRow

		#region Get Game Rating

		/// <summary>
		/// Queries the ESRB website, retrieves a HTTP stream and parses the stream.
		/// </summary>
		/// <param name="url">Url to query</param>
		/// <returns>GameRatingDetail for a specific game</returns>
		public GameRatingDetail GetGameRating(string url)
		{
			GameRatingDetailsCollection ratings;
			WebResponse response;
			Stream stream;
			StreamReader reader;
			HttpWebRequest request;

			try
			{
				request = (HttpWebRequest) WebRequest.Create(url);

				response = request.GetResponse();
				stream = response.GetResponseStream();

				if (!response.ContentType.ToLower().StartsWith("text/"))
					return null;

				reader = new StreamReader(stream);
				ratings = FilterRatings(GetRatings(reader));
			}
			catch
			{
				return null;
			}
			finally
			{
				response = null;
				stream = null;
				reader = null;
				request = null;
			}

			if (ratings.Count <= 0)
				return null;

			// If the system is set to trim all non-exact results we need to remove them here.
			if (TrimNonExactRatingResults && name != null)
			{
				for (int i = 0; i < ratings.Count; i++)
				{
					if (ratings[i].Name.Trim().ToLower() != name.Trim().ToLower())
					{
						ratings.RemoveAt(i);
						i--;
					}
				}
			}

			if (ratings.Count == 1) // Only one match, so take it.
			{
				ratings[0].Exact = true;
				return ratings[0];
			}
			else if (ratings.AllSameRating()) // Are all the ratings the same?
			{
				ratings[0].Exact = false;
				return ratings[0];
			}

			return null; // The client will have to ask again.
		}

		#endregion Get Game Rating

		#region Filter Ratings

		/// <summary>
		/// Filters the ratings returned from the ESRB's ratings
		/// </summary>
		/// <param name="ratings">GameRatingDetailsCollection to strip</param>
		/// <returns>Collection or GameRatingDetails for only WindowsPC</returns>
		private GameRatingDetailsCollection FilterRatings(GameRatingDetailsCollection ratings)
		{
			GameRatingDetailsCollection justPC = new GameRatingDetailsCollection();

			foreach (GameRatingDetail o in ratings)
			{
				if (o.Platforms.Contains("Windows PC"))
					justPC.Add(o);
			}

			return justPC;
		}

		#endregion Filter Ratings

		#region Get Ratings

		/// <summary>
		/// Gets the ratings from the ESRB sites table
		/// </summary>
		/// <param name="reader">Stream Reader of the Websites HTTP</param>
		/// <returns>Collection or GameRatingDetails</returns>
		private GameRatingDetailsCollection GetRatings(StreamReader reader)
		{
			GameRatingDetailsCollection ratings = new GameRatingDetailsCollection();
			string line;

			while (null != (line = reader.ReadLine()))
			{
				if (IsRatingTableRow(line))
					ratings.Add(ProcessRatingTableRow(reader));
			}

			return ratings;
		}

		#endregion Get Ratings

		#region Query ESRB

		private GameRatingDetail QueryESRB(string title, string publisher, string fileName, string directoryName,
		                                   string productName)
		{
			string fileNameMinusExt;
			string query;
			GameRatingDetail rating = null;
			Rating stored = null;

			if (fileName.IndexOf(".") >= 0)
				fileNameMinusExt = fileName.Remove(fileName.IndexOf("."), 4);
			else
				fileNameMinusExt = fileName;

			if (ConfigurationManager.AppSettings["CheckLocalDatabase"] != null &&
			    bool.Parse(ConfigurationManager.AppSettings["CheckLocalDatabase"]))
			{
				try
				{
					// Local Check....
					stored = RatingsDAO.GetRating(title, fileName, productName);
				}
				catch
				{
					/* Eat Exceptions */
				}
			}

			if (stored != null)
			{
				rating = new GameRatingDetail(stored.GameName, stored.Publisher, stored.EsrbRating, stored.GameContent,
				                              stored.Platforms);
				rating.Exact = false;
			}

			if ((rating == null) && ((title != null && title.Length > 0) && (publisher != null && publisher.Length > 0)))
			{
				name = title;
				query = "http://www.esrb.org/ratings/search.jsp?title=" + HttpUtility.UrlEncode(title) + "&publisher=" +
				        HttpUtility.UrlEncode(publisher);
				rating = GetGameRating(query);
			}
			if ((rating == null) && (title != null && title.Length > 0))
			{
				name = title;
				query = "http://www.esrb.org/ratings/search.jsp?title=" + HttpUtility.UrlEncode(title);
				rating = GetGameRating(query);

				if (rating == null)
				{
					string[] NamesToCheck = NameCleaner.GetNameParts(title);

					foreach (string s in NamesToCheck)
					{
						name = s;
						query = "http://www.esrb.org/ratings/search.jsp?title=" + HttpUtility.UrlEncode(s);
						rating = GetGameRating(query);

						if (rating != null)
							break;
					}
				}
			}
			if ((rating == null) && (directoryName != null && directoryName.Length > 0))
			{
				name = directoryName;
				query = "http://www.esrb.org/ratings/search.jsp?title=" + HttpUtility.UrlEncode(directoryName);
				rating = GetGameRating(query);

				if (rating == null)
				{
					string[] NamesToCheck = NameCleaner.GetNameParts(directoryName);

					foreach (string s in NamesToCheck)
					{
						query = "http://www.esrb.org/ratings/search.jsp?title=" + HttpUtility.UrlEncode(s);
						rating = GetGameRating(query);

						if (rating != null)
							break;
					}
				}
			}
			if ((rating == null) && (fileName != null && fileName.Length > 0))
			{
				name = fileNameMinusExt;
				query = "http://www.esrb.org/ratings/search.jsp?title=" + HttpUtility.UrlEncode(fileNameMinusExt);
				rating = GetGameRating(query);
			}

			if (rating != null)
			{
				if (rating.Exact.HasValue && rating.Exact.Value)
					try
					{
						RatingsDAO.AddNewRating(rating, title, publisher, fileName, directoryName, productName);
					}
					catch
					{
					}
			}

			name = null;
			return rating;
		}

		#endregion Query ESRB

		#region Get ESRB Rating

		public int? GetESRBRating(string title, string publisher, string fileName, string directoryName, string productName)
		{
			GameRatingDetail rating = QueryESRB(title, publisher, fileName, directoryName, productName);

			if (rating != null)
				return (int) rating.GetRatingType();
			else
				return null;
		}

		public int? GetESRBRating(string title, string publisher, string fileName, string directoryName, string productName,
		                          bool trimResults)
		{
			TrimNonExactRatingResults = trimResults;
			GameRatingDetail rating = QueryESRB(title, publisher, fileName, directoryName, productName);

			if (rating != null)
				return (int) rating.GetRatingType();
			else
				return null;
		}

		#endregion Get ESRB Rating

		#region Get ESRB Game Detail

		public GameRatingDetail GetESRBGameDetail(string title, string publisher, string fileName, string directoryName,
		                                          string productName)
		{
			return QueryESRB(title, publisher, fileName, directoryName, productName);
		}

		public GameRatingDetail GetESRBGameDetail(string title, string publisher, string fileName, string directoryName,
		                                          string productName, bool trimResults)
		{
			TrimNonExactRatingResults = trimResults;
			return QueryESRB(title, publisher, fileName, directoryName, productName);
		}

		#endregion Get ESRB Game Detail
	}
}